﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

using log4net;
using ConfigUtilLib;
using CommonLib;
using QueueMgrUtil;


namespace DataFetcherLib
{
    /// <summary>
    /// Purpose of this class is to Read Configuration 
    /// Start fetching data and adding to correct models 
    /// and adding a processing Job to the Queue
    /// Btw , this is singleton class 
    /// </summary>
    public class FetcherManager
    {
        #region PRIVATE_MEMBERS_CONSTRUCTOR
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private static FetcherManager _datafetcherMgr = null;
        private BlockReadQueue<QueueWorkItem> _processingQueue = null;
        private  CountdownEvent _countdown=null;
        private AppConfig _appConfig = null;
        #endregion 

        

        #region PUBLIC_INTERFACE
        /// <summary>
        /// getDatafetcherSingletonObject : Dude Self Explanatory 
        /// If you want Document for this , I knwo for sure you are from LockHeed martin
        /// or some Defense Company where US GOV pays for Doc. 
        /// Good Luck with Open Source
        /// </summary>
        /// <returns></returns>
        public static FetcherManager getDatafetcherSingletonObject()
        {
            if (_datafetcherMgr == null)
            {
                _datafetcherMgr = new FetcherManager();
            }

            return _datafetcherMgr;
        }
        /// <summary>
        /// Constrtuctor : Init lot of good Stuff.. 
        /// Please follow Documentation inside the prodecure (a.k.a method  for JJJAAAVVVAAA Guys :-) ) 
        /// </summary>
        public FetcherManager()
        {

        }

        public void KickOffDataFetcher(AppConfig appCfg)
        {
            _processingQueue = QueueManager.WorkItemsQueue;
           _countdown = new CountdownEvent(appCfg.FetcherRecords.Count);
           _appConfig = appCfg;
           ThreadPool.QueueUserWorkItem(RunDatafetcherThread);
        }

        private static void RunDatafetcherThread(Object args)
        {
            FetcherManager fetchManager = FetcherManager.getDatafetcherSingletonObject();
           
            // Loop thru Configuration for all Frameworks and Fetch Data 
            foreach (FetcherRecord fetcherRec in fetchManager._appConfig.FetcherRecords)
            {                
                if (!ThreadPool.QueueUserWorkItem(GoFetchData, fetcherRec))
                {
                    Console.Error.WriteLine("Failed to Queue Fetcher to Thread.. {0}", fetcherRec.FetcherName);
                }
                else
                {
                    log.DebugFormat("Queued to Fetcher Thread.. :  {0}", fetcherRec.FetcherName);
                }
                
            }

            // Queue End of the work Queue Item 
            fetchManager._countdown.Wait();
            QueueWorkItem endoftheWork = new QueueWorkItem(QUEUE_WORK_ITEM_TYPE.PROCESS_END, null, null);
            fetchManager._processingQueue.Enqueue(endoftheWork);

        }

       
        #endregion 

        #region PRIVATE_UTILS_HELPERS
        private static void GoFetchData( object fetchRecordObject )
        {

            FetcherRecord fetchRec = (FetcherRecord)fetchRecordObject;
            FetcherManager fetchMgr = FetcherManager.getDatafetcherSingletonObject();
            log.DebugFormat(" Fetching Data for  fetch Record :" + fetchRec.FetcherName);

            //  perform Fecthing Operation 

            if (fetchMgr._processingQueue != null)
            {
                // Perform Fetching Based on Input Coming From fetcher Record

                RawCrawledData rawData = new RawCrawledData();

                if (!RedFinCrawler.crawlForResults(fetchRec,  rawData))
                {
                    log.DebugFormat("Failed to Crawl Redfin for {0}",fetchRec.FetcherName);
                }

                QueueWorkItem workItemToProcess = new QueueWorkItem(QUEUE_WORK_ITEM_TYPE.PROCESS_WORK_ITEM, fetchRec, rawData );
                fetchMgr._processingQueue.Enqueue(workItemToProcess);
                // Signal Back 
                fetchMgr._countdown.Signal();
            }
            
        }

        #endregion 
    }
}
